package org.wmq.taskschedule.util;

import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.opencv.core.Mat;
import org.opencv.imgcodecs.Imgcodecs;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * MinIO服务类
 * 提供对MinIO的增删改查操作，以及处理OpenCV的Mat类型的特殊方法
 */
@Service
@Slf4j
public class MinioService {

    private final MinioClient minioClient;

    @Value("${minio.bucket-name:default-bucket}")
    private String defaultBucketName;

    /**
     * 构造函数，通过依赖注入获取MinIO客户端
     * 
     * @param minioClient MinIO客户端实例
     */
    public MinioService(MinioClient minioClient) {
        this.minioClient = minioClient;
    }

    /**
     * 创建桶
     * 
     * @param bucketName 桶名称
     * @throws Exception 创建桶过程中的异常
     */
    public void createBucket(String bucketName) throws Exception {
        boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        if (!exists) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
            log.info("创建桶成功: {}", bucketName);
        } else {
            log.info("桶已存在: {}", bucketName);
        }
    }

    /**
     * 获取所有桶
     * 
     * @return 桶列表
     * @throws Exception 获取桶列表过程中的异常
     */
    public List<String> getAllBuckets() throws Exception {
        List<Bucket> buckets = minioClient.listBuckets();
        List<String> bucketNames = new ArrayList<>();
        for (Bucket bucket : buckets) {
            bucketNames.add(bucket.name());
        }
        return bucketNames;
    }

    /**
     * 上传文件
     * 
     * @param file 文件对象
     * @return 文件名
     * @throws Exception 上传过程中的异常
     */
    public String uploadFile(MultipartFile file) throws Exception {
        return uploadFile(defaultBucketName, file);
    }

    /**
     * 上传文件到指定桶
     * 
     * @param bucketName 桶名称
     * @param file 文件对象
     * @return 文件名
     * @throws Exception 上传过程中的异常
     */
    public String uploadFile(String bucketName, MultipartFile file) throws Exception {
        if (file == null || file.isEmpty()) {
            throw new IllegalArgumentException("文件不能为空");
        }

        // 确保桶存在
        createBucket(bucketName);

        // 使用原始文件名作为对象名
        String objectName = file.getOriginalFilename();
        if (objectName == null) {
            objectName = "upload_" + System.currentTimeMillis();
        }

        // 上传文件
        minioClient.putObject(
                PutObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .stream(file.getInputStream(), file.getSize(), -1)
                        .contentType(file.getContentType())
                        .build()
        );

        log.info("文件上传成功: {} 到桶: {}", objectName, bucketName);
        return objectName;
    }

    /**
     * 上传文件（字节数组）
     * 
     * @param bucketName 桶名称
     * @param objectName 对象名称
     * @param content 文件内容字节数组
     * @param contentType 内容类型
     * @return 文件名
     * @throws Exception 上传过程中的异常
     */
    public String uploadFile(String bucketName, String objectName, byte[] content, String contentType) throws Exception {
        // 确保桶存在
        createBucket(bucketName);

        // 上传文件
        minioClient.putObject(
                PutObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .stream(new ByteArrayInputStream(content), content.length, -1)
                        .contentType(contentType)
                        .build()
        );

        log.info("文件上传成功: {} 到桶: {}", objectName, bucketName);
        return objectName;
    }

    /**
     * 下载文件
     * 
     * @param objectName 对象名称
     * @return 文件字节数组
     * @throws Exception 下载过程中的异常
     */
    public byte[] downloadFile(String objectName) throws Exception {
        return downloadFile(defaultBucketName, objectName);
    }

    /**
     * 从指定桶下载文件
     * 
     * @param bucketName 桶名称
     * @param objectName 对象名称
     * @return 文件字节数组
     * @throws Exception 下载过程中的异常
     */
    public byte[] downloadFile(String bucketName, String objectName) throws Exception {
        try (
                InputStream stream = minioClient.getObject(
                        GetObjectArgs.builder()
                                .bucket(bucketName)
                                .object(objectName)
                                .build()
                );
                ByteArrayOutputStream outputStream = new ByteArrayOutputStream()
        ) {
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = stream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
            return outputStream.toByteArray();
        }
    }

    /**
     * 删除文件
     * 
     * @param objectName 对象名称
     * @throws Exception 删除过程中的异常
     */
    public void deleteFile(String objectName) throws Exception {
        deleteFile(defaultBucketName, objectName);
    }

    /**
     * 从指定桶删除文件
     * 
     * @param bucketName 桶名称
     * @param objectName 对象名称
     * @throws Exception 删除过程中的异常
     */
    public void deleteFile(String bucketName, String objectName) throws Exception {
        minioClient.removeObject(
                RemoveObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .build()
        );
        log.info("文件删除成功: {} 从桶: {}", objectName, bucketName);
    }

    /**
     * 列出指定桶中的所有文件
     * 
     * @param bucketName 桶名称
     * @return 文件列表
     * @throws Exception 列出文件过程中的异常
     */
    public List<String> listFiles(String bucketName) throws Exception {
        List<String> objectNames = new ArrayList<>();
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder()
                        .bucket(bucketName)
                        .recursive(true)
                        .build()
        );

        for (Result<Item> result : results) {
            Item item = result.get();
            objectNames.add(item.objectName());
        }

        return objectNames;
    }

    /**
     * 生成文件的临时访问URL
     * 
     * @param objectName 对象名称
     * @param expires 过期时间（秒）
     * @return 临时访问URL
     * @throws Exception 生成URL过程中的异常
     */
    public String getPresignedUrl(String objectName, int expires) throws Exception {
        return getPresignedUrl(defaultBucketName, objectName, expires);
    }

    /**
     * 生成指定桶中文件的临时访问URL
     * 
     * @param bucketName 桶名称
     * @param objectName 对象名称
     * @param expires 过期时间（秒）
     * @return 临时访问URL
     * @throws Exception 生成URL过程中的异常
     */
    public String getPresignedUrl(String bucketName, String objectName, int expires) throws Exception {
        return minioClient.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs.builder()
                        .method(Method.GET)
                        .bucket(bucketName)
                        .object(objectName)
                        .expiry(expires, TimeUnit.SECONDS)
                        .build()
        );
    }

    /**
     * 特殊方法：上传OpenCV的Mat对象到MinIO
     * 
     * @param mat OpenCV的Mat对象
     * @return 生成的对象名称
     * @throws Exception 上传过程中的异常
     */
    public String uploadMat(Mat mat) throws Exception {
        return uploadMat(defaultBucketName, mat);
    }

    /**
     * 特殊方法：上传OpenCV的Mat对象到指定桶
     * 
     * @param bucketName 桶名称
     * @param mat OpenCV的Mat对象
     * @return 生成的对象名称
     * @throws Exception 上传过程中的异常
     */
    public String uploadMat(String bucketName, Mat mat) throws Exception {
        if (mat == null || mat.empty()) {
            throw new IllegalArgumentException("Mat对象不能为空");
        }

        // 自动生成对象名称
        String objectName = "mat_" + System.currentTimeMillis() + ".jpg";

        // 获取文件格式
        String format = objectName.substring(objectName.lastIndexOf(".") + 1);

        // 将Mat转换为字节数组
        org.opencv.core.MatOfByte matOfByte = new org.opencv.core.MatOfByte();
        Imgcodecs.imencode("." + format, mat, matOfByte);
        byte[] imageBytes = matOfByte.toArray();

        // 确保桶存在
        createBucket(bucketName);

        // 上传文件
        minioClient.putObject(
                PutObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .stream(new ByteArrayInputStream(imageBytes), imageBytes.length, -1)
                        .contentType("image/" + format.toLowerCase())
                        .build()
        );
        
        log.info("Mat对象上传成功: {} 到桶: {}", objectName, bucketName);
        return objectName;
    }

    /**
     * 特殊方法：从MinIO下载文件并转换为OpenCV的Mat对象
     * 
     * @param objectName 对象名称
     * @return OpenCV的Mat对象
     * @throws Exception 下载过程中的异常
     */
    public Mat downloadMat(String objectName) throws Exception {
        return downloadMat(defaultBucketName, objectName);
    }

    /**
     * 特殊方法：从指定桶下载文件并转换为OpenCV的Mat对象
     * 
     * @param bucketName 桶名称
     * @param objectName 对象名称
     * @return OpenCV的Mat对象
     * @throws Exception 下载过程中的异常
     */
    public Mat downloadMat(String bucketName, String objectName) throws Exception {
        byte[] data = downloadFile(bucketName, objectName);
        return Imgcodecs.imdecode(new org.opencv.core.MatOfByte(data), Imgcodecs.IMREAD_UNCHANGED);
    }

    /**
     * 检查文件是否存在
     * 
     * @param objectName 对象名称
     * @return 是否存在
     */
    public boolean fileExists(String objectName) {
        return fileExists(defaultBucketName, objectName);
    }

    /**
     * 检查指定桶中文件是否存在
     * 
     * @param bucketName 桶名称
     * @param objectName 对象名称
     * @return 是否存在
     */
    public boolean fileExists(String bucketName, String objectName) {
        try {
            minioClient.statObject(
                    StatObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .build()
            );
            return true;
        } catch (Exception e) {
            // 如果文件不存在，MinIO会抛出异常
            return false;
        }
    }
}